﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using SimpleIR;

namespace InterpreterCore
{
	/// <summary>
	/// Representation of a simple pointer.
	/// </summary>
	internal struct PtrValue
	{
		/// <summary>
		/// The address of the object.
		/// </summary>
		internal readonly int TargetAddress;

		/// <summary>
		/// The type of the pointer.
		/// </summary>
		internal readonly IRPtrType ValType;

		internal PtrValue(int trgtaddr, IRPtrType ptype)
		{
			this.TargetAddress = trgtaddr;
			this.ValType = ptype;
		}

		internal static PtrValue MakeNullPtr(IRPtrType nullptr)
		{ return new PtrValue(MemoryState.NULL_ADDRESS, nullptr); }

		internal bool IsNullPtr
		{ get { return this.TargetAddress == MemoryState.NULL_ADDRESS; } }

		public override string ToString()
		{
			if(this.ValType == null)
				return "Invalid!!";
			else if(this.TargetAddress == MemoryState.NULL_ADDRESS)
				return "null";
			else
				return String.Format("{0}", this.TargetAddress);
		}

		/// <summary>
		/// The value formatted as a string for display in the debugger.
		/// </summary>
		internal string DebuggerString()
		{
			Debug.Assert(this.ValType != null);

			if(this.TargetAddress == MemoryState.NULL_ADDRESS)
				return "null";
			else
				return String.Format("ptr@{0}", this.TargetAddress);
		}

		/// <summary>
		/// The value formatted as a string in a format for emitting in the trace.
		/// </summary>
		internal string EmitForTrace()
		{
			Debug.Assert(this.ValType != null);

			return this.TargetAddress.ToString();
		}

		/// <summary>
		/// The value formatted and output for a heap dump.
		/// </summary>
		internal void SerializeForHeapDump(System.IO.TextWriter dumpstr)
		{
			Debug.Assert(this.ValType != null);

			dumpstr.Write(this.TargetAddress);
		}

		/// <summary>
		/// Parse value of the given type from a string.
		/// </summary>
		internal static PtrValue ParseValue(IRPtrType pvt, string val)
		{
			return new PtrValue(Int32.Parse(val), pvt);
		}
	}

	/// <summary>
	/// Representation of a simple pointer.
	/// </summary>
	internal struct ReferenceValue
	{
		/// <summary>
		/// The address of the object.
		/// </summary>
		internal readonly int TargetAddress;

		/// <summary>
		/// The offset that the reference points to in the given address.
		/// </summary>
		internal readonly IRBasicAccessPath TargetOffset;

		/// <summary>
		/// If the first element in the offset is an array contents field then this is the array index to use.
		/// </summary>
		internal readonly int OptArrayIndex;

		/// <summary>
		/// The type of the reference.
		/// </summary>
		internal readonly IRReferenceType ValType;

		internal ReferenceValue(int trgtaddr, IRBasicAccessPath trgtoffset, int opttrgtindex, IRReferenceType rtype)
		{
			this.TargetAddress = trgtaddr;
			this.TargetOffset = trgtoffset;
			this.OptArrayIndex = opttrgtindex;
			this.ValType = rtype;
		}

		internal static ReferenceValue MakeNullReference(IRReferenceType oftype)
		{ return new ReferenceValue(MemoryState.NULL_ADDRESS, IRBasicAccessPath.EmptyAccessPath, -1, oftype); }

		internal bool IsNullReference
		{ get { return this.TargetAddress == MemoryState.NULL_ADDRESS; } }

		internal static bool AreRefTargetsEqual(ReferenceValue r1, ReferenceValue r2)
		{ return (r1.TargetAddress == r2.TargetAddress) & (r1.TargetOffset == r2.TargetOffset) & (r1.OptArrayIndex == r2.OptArrayIndex); }

		public override string ToString()
		{
			if(this.ValType == null)
				return "Invalid!!";
			else if(this.TargetAddress == MemoryState.NULL_ADDRESS)
				return "nullref";
			else if(this.OptArrayIndex == -1)
				return String.Format("{0}.{1}", this.TargetAddress, this.TargetOffset.FieldOnlyDisplayString());
			else
			{
				if(this.TargetOffset.Path.Length == 1)
					return String.Format("{0}[{1}]", this.TargetAddress, this.OptArrayIndex);
				else
					return String.Format("{0}[{1}].{2}", this.TargetAddress, this.OptArrayIndex, this.TargetOffset.Tail.FieldOnlyDisplayString());
			}
		}

		/// <summary>
		/// The value formatted as a string for display in the debugger.
		/// </summary>
		internal string DebuggerString()
		{
			Debug.Assert(this.ValType != null);

			if(this.TargetAddress == MemoryState.NULL_ADDRESS)
				return "nullref";
			else if(this.OptArrayIndex == -1)
				return String.Format("{0}.{1}", this.TargetAddress, this.TargetOffset.FieldOnlyDisplayString());
			else
			{
				if(this.TargetOffset.Path.Length == 1)
					return String.Format("{0}[{1}]", this.TargetAddress, this.OptArrayIndex);
				else
					return String.Format("{0}[{1}].{2}", this.TargetAddress, this.OptArrayIndex, this.TargetOffset.Tail.FieldOnlyDisplayString());
			}
		}

		/// <summary>
		/// The value formatted as a string in a format for emitting in the trace.
		/// </summary>
		internal string EmitForTrace()
		{
			Debug.Assert(this.ValType != null);

			return String.Format("{0}@{1}${2}[{3}]", this.TargetAddress, this.TargetOffset.PathID, this.TargetOffset.FieldOnlyDisplayString(), this.OptArrayIndex);
		}

		/// <summary>
		/// The value formatted and output for a heap dump.
		/// </summary>
		internal void SerializeForHeapDump(System.IO.TextWriter dumpstr)
		{
			Debug.Assert(this.ValType != null);

			string rvs = String.Format("{0}@{1}[{2}]", this.TargetAddress, this.TargetOffset.PathID, this.OptArrayIndex);
			dumpstr.Write(rvs);
		}

		/// <summary>
		/// Parse value of the given type from a string.
		/// </summary>
		internal static ReferenceValue ParseValue(IRReferenceType rvt, string val)
		{
			int apos = val.IndexOf('@');
			int dpos = val.IndexOf('$');
			int lapos = val.IndexOf('[');
			int pidend = (dpos != -1) ? dpos : lapos;

			string addrstr = val.Substring(0, apos);
			int addr = Int32.Parse(addrstr);

			string pathposstr = val.Substring(apos + 1, pidend - (apos + 1));
			int pathid = Int32.Parse(pathposstr);
			IRBasicAccessPath offset = GlobalInterpreterState.TProg.GetAccessPathWithID(pathid);

			string arrayposstr = val.Substring(pidend + 1, val.Length - (pidend + 2));
			int arrayidx = Int32.Parse(arrayposstr);

			return new ReferenceValue(addr, offset, arrayidx, rvt);
		}
	}
}
